Skip to content

feat: weekly report#177

Merged
hmbanan666 merged 2 commits into
mainfrom
weekly-report
Sep 22, 2025
Merged

feat: weekly report#177
hmbanan666 merged 2 commits into
mainfrom
weekly-report

Conversation

@hmbanan666
Copy link
Copy Markdown
Collaborator

@hmbanan666 hmbanan666 commented Sep 22, 2025

Summary by CodeRabbit

  • New Features
    • Automated weekly AI-generated report added, scheduled every Friday at 17:30; weekly reports appear in the flow labeled with the current week number.
  • Chores
    • New environment variable to customize the weekly report prompt: NUXT_AI_WEEKLY_REPORT_PROMPT.
  • Improvements
    • Weekly report generation enhanced with broader task aggregation and more robust AI request handling.
  • Documentation
    • Clarified daily report task description by removing Telegram-specific wording.

@hmbanan666 hmbanan666 self-assigned this Sep 22, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Sep 22, 2025

Walkthrough

Adds a weekly AI report: new env var and runtime config prompt, a scheduled Nitro task (Fridays 17:30), a new server task that aggregates weekly completed tasks and calls OpenAI to create a weekly_task_report flow item, a DB method to list weekly completions, and a FlowItemType update.

Changes

Cohort / File(s) Summary of Changes
Env & Runtime Config
apps/web-app/.env.example, apps/web-app/nuxt.config.ts
Adds NUXT_AI_WEEKLY_REPORT_PROMPT; adds runtimeConfig.ai.weeklyReportPrompt; registers Nitro scheduled task ai:weekly-report at Fri 17:30.
AI Tasks
apps/web-app/server/tasks/ai/daily-report.ts, apps/web-app/server/tasks/ai/weekly-report.ts
Updates daily task description and OpenAI client options (timeout, maxRetries); adds new ai:weekly-report task that collects this week's completed tasks, builds payload with performer metadata, queries OpenAI using the weekly prompt, and creates a weekly_task_report flow item.
Database Layer & Types
packages/database/src/repository/task.ts, packages/database/src/types.ts
Adds Task.listCompletedThisWeek() (completedAt within last 6 days, ordered by updatedAt desc); extends FlowItemType to include 'weekly_task_report'.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant CRON as Scheduler (Nitro)
    participant Task as ai:weekly-report Task
    participant DB as Database
    participant AI as OpenAI Client
    participant Flow as Flow Repo

    CRON->>Task: Trigger (Fri 17:30)
    rect rgba(200,230,255,0.35)
      Note over Task: Prepare payload
      Task->>DB: Task.listCompletedThisWeek()
      DB-->>Task: Completed tasks
      Task->>DB: Fetch staff/performer data
      DB-->>Task: Staff info
    end
    rect rgba(220,255,220,0.35)
      Note over AI: Generate weekly summary
      Task->>AI: chat.completions (system: weeklyReportPrompt, user: tasks JSON)
      AI-->>Task: finalMessage
    end
    rect rgba(255,240,200,0.35)
      Note over Flow: Persist report
      Task->>Flow: Create item (type: weekly_task_report, title with week, description)
      Flow-->>Task: OK
    end
    Task-->>CRON: Success

    alt Error path
      Task-->>CRON: errorResolver -> success=false
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • chore: some updates #165 — Also modifies apps/web-app/server/tasks/ai/daily-report.ts; overlaps OpenAI client/runtime adjustments.
  • chore: navigation rework #152 — Adjusts daily-report task strings and related task behavior, intersecting with the daily task change here.
  • chore: tasks rework #88 — Changes AI prompt/config surface and .env.example, related to adding the weekly prompt field.

Poem

A rabbit taps keys in a Friday groove,
Wrangles the week into tidy prose and move.
Tasks hop in lines, names gathered neat,
A prompt whispers stories of work complete.
Flow item blooms—weekly report, carrot-celebrate! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "feat: weekly report" accurately captures the primary change in this branch — introduction of a weekly AI report task along with related runtime config, environment variable, and database/type updates; it is concise and directly related to the changeset. The title avoids noisy file lists or vague wording and highlights the main feature from the developer's perspective. A teammate skimming PR history will understand the primary intent.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch weekly-report

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
apps/web-app/.env.example (1)

27-27: Add brief guidance for the new prompt var.

Consider a one‑line comment above with expected format (tone, JSON schema, language) to reduce misconfiguration.

apps/web-app/server/tasks/ai/weekly-report.ts (3)

26-51: Avoid O(N²) counting and repeat lookups; precompute maps.

preparePerformer() filters tasks on every call. Build staffById and completedCounts once, then reuse.

Apply this diff:

-      function preparePerformer(performerId: string | null) {
-        const user = staff.find((user) => user.id === performerId)
-        if (!user) {
-          return null
-        }
-
-        return {
-          name: user?.name,
-          surname: user?.surname,
-          caption: user?.caption,
-          completedTasksCount: tasks.filter((task) => task.performerId === user?.id).length,
-        }
-      }
+      const staffById = new Map(staff.map(u => [u.id, u]))
+      const completedCounts = new Map<string, number>()
+      for (const t of tasks) {
+        if (!t.performerId) continue
+        completedCounts.set(t.performerId, (completedCounts.get(t.performerId) ?? 0) + 1)
+      }
+
+      function preparePerformer(performerId: string | null) {
+        if (!performerId) return null
+        const user = staffById.get(performerId)
+        if (!user) return null
+        return {
+          name: user.name,
+          surname: user.surname,
+          caption: user.caption,
+          completedTasksCount: completedCounts.get(user.id) ?? 0,
+        }
+      }

71-74: Defensive read of completion result + log empty.

Guard against empty choices and add a warning for observability.

Apply this diff:

-      const finalMessage = response.choices[0].message.content
-      if (!finalMessage) {
-        return { result: true }
-      }
+      const finalMessage = response.choices?.[0]?.message?.content?.trim()
+      if (!finalMessage) {
+        logger.warn('ai:weekly-report returned empty content')
+        return { result: true }
+      }

83-88: Error handling policy: success on failure hides issues.

Catching and then returning success can mask outages. Consider returning { result: false } or emitting an alert/metric.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 42ed5b4 and c4cab6c.

📒 Files selected for processing (6)
  • apps/web-app/.env.example (1 hunks)
  • apps/web-app/nuxt.config.ts (2 hunks)
  • apps/web-app/server/tasks/ai/daily-report.ts (1 hunks)
  • apps/web-app/server/tasks/ai/weekly-report.ts (1 hunks)
  • packages/database/src/repository/task.ts (1 hunks)
  • packages/database/src/types.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
apps/web-app/server/tasks/ai/weekly-report.ts (1)
packages/database/src/tables.ts (1)
  • tasks (326-338)
packages/database/src/repository/task.ts (1)
packages/database/src/tables.ts (1)
  • tasks (326-338)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (8)
apps/web-app/server/tasks/ai/daily-report.ts (1)

12-12: Copy edit: description aligns with current behavior.

Removing Telegram mention is correct given the task posts to Flow only.

apps/web-app/nuxt.config.ts (2)

22-22: Config surface LGTM.

weeklyReportPrompt correctly mirrors daily.


85-85: Confirm cron timezone.

Nitro uses the server’s TZ. Verify “Friday 17:30” matches your business TZ; otherwise, set container TZ or adjust schedule.

apps/web-app/server/tasks/ai/weekly-report.ts (3)

15-18: Production guard: LGTM.

Prevents accidental runs in dev/test.


76-82: Align title’s “week” with DB window.

You label by local week number (w, ru locale) but the query currently uses a rolling 7‑day window. After switching the query to date_trunc('week', now()), consider ISO week (I) to avoid locale surprises, or keep w if that matches business rules—just be consistent.

-      const week = format(new Date(), 'w', { locale: ru })
+      const week = format(new Date(), 'I', { locale: ru }) // ISO week number

23-25: Sanity check — ensure repository.user.findStaff() returns id, name, surname, caption.
Method exists at packages/database/src/repository/user.ts (static async findStaff, ~line 38); confirm it selects or maps id, name, surname and caption because apps/web-app/server/tasks/ai/weekly-report.ts expects those fields.

packages/database/src/repository/task.ts (1)

77-82: Use calendar-week bounds (not a rolling 7‑day window) and order by completedAt

File: packages/database/src/repository/task.ts (lines 77-82)
now() - interval '6 day' creates a rolling 7‑day window; switch to calendar-week bounds and sort by completion time.

   static async listCompletedThisWeek() {
-    return useDatabase().query.tasks.findMany({
-      where: (tasks, { gte }) => gte(tasks.completedAt, sql`now() - interval '6 day'`),
-      orderBy: (tasks, { desc }) => desc(tasks.updatedAt),
-    })
+    return useDatabase().query.tasks.findMany({
+      where: (tasks, { and, gte, lt }) =>
+        and(
+          gte(tasks.completedAt, sql`date_trunc('week', now())`),
+          lt(tasks.completedAt, sql`date_trunc('week', now() + interval '7 day')`),
+        ),
+      orderBy: (tasks, { desc }) => desc(tasks.completedAt),
+    })
   }

If production uses a non‑UTC timezone, confirm the business definition of “week” (we can adapt the SQL with AT TIME ZONE or a specific TZ if required).

packages/database/src/types.ts (1)

39-39: Approve — FlowItemType added; no DB enum/check found.

flow_items.type is defined as varchar(...) in packages/database/src/tables.ts:808 and FlowItemType is at packages/database/src/types.ts:39; I found no CREATE TYPE / ALTER TABLE / CHECK enforcing an enum for this column, and weekly_task_report is only used in types + apps/web-app/server/tasks/ai/weekly-report.ts:79 — no DB migration required.

Comment thread apps/web-app/server/tasks/ai/weekly-report.ts
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web-app/server/tasks/ai/daily-report.ts (1)

83-88: Surface failures to the scheduler.

Same masking pattern as weekly; return success even on error.

Apply:

     } catch (error) {
       errorResolver(error)
-    }
-
-    return { result: true }
+      throw error
+    }
+
+    return { result: true }
🧹 Nitpick comments (7)
apps/web-app/server/tasks/ai/weekly-report.ts (5)

23-25: Skip OpenAI call when there’s nothing to summarize.

Avoid paid call and flow write on empty weeks.

Apply:

       const tasks = await repository.task.listCompletedThisWeek()
       const staff = await repository.user.findStaff()

+      if (tasks.length === 0) {
+        logger.info('No completed tasks this week; skipping AI call and flow item creation')
+        return { result: true }
+      }

26-38: Avoid O(n²) scans per performer; preindex staff and counts.

Precompute maps; current filter-per-task per user is quadratic.

Apply within this function:

-      function preparePerformer(performerId: string | null) {
-        const user = staff.find((user) => user.id === performerId)
-        if (!user) {
-          return null
-        }
-
-        return {
-          name: user?.name,
-          surname: user?.surname,
-          caption: user?.caption,
-          completedTasksCount: tasks.filter((task) => task.performerId === user?.id).length,
-        }
-      }
+      function preparePerformer(performerId: string | null) {
+        if (!performerId) return null
+        const user = staffMap.get(performerId)
+        if (!user) return null
+        return {
+          name: user.name,
+          surname: user.surname,
+          caption: user.caption,
+          completedTasksCount: completedCounts.get(performerId) ?? 0,
+        }
+      }

Add just above this function:

const staffMap = new Map(staff.map(u => [u.id, u]))
const completedCounts = tasks.reduce((acc, t) => {
  if (t.performerId) acc.set(t.performerId, (acc.get(t.performerId) ?? 0) + 1)
  return acc
}, new Map<string, number>())

59-71: Bound cost and increase determinism.

Set low temperature and cap tokens; also tag requests with user.

Apply:

       const response = await client.chat.completions.create({
         model: ai.modelPro,
+        user: 'ai:weekly-report',
+        temperature: 0.2,
+        max_tokens: 800,
         messages: [

If the weekly payload can grow, consider chunking tasks and asking the model to merge summaries.


73-76: Defensive parse of completion and finish reason.

Avoid empty/unfinished responses.

Apply:

-      const finalMessage = response.choices[0].message.content
-      if (!finalMessage) {
+      const choice = response.choices[0]
+      const finalMessage = choice.message.content?.trim()
+      if (!finalMessage || choice.finish_reason !== 'stop') {
+        logger.warn('OpenAI response missing content or not finished: %o', { finish_reason: choice.finish_reason })
         return { result: true }
       }

79-84: Use ISO/padded week for stable titles.

'w' is locale‑week and unpadded. Prefer ISO ('II') or at least padded locale ('ww').

Apply one:

-      const week = format(new Date(), 'w', { locale: ru })
+      const week = format(new Date(), 'II') // ISO week 01–53
apps/web-app/server/tasks/ai/daily-report.ts (2)

23-24: Short‑circuit on empty day.

Skip OpenAI call when no completed tasks.

Apply:

       const tasks = await repository.task.listCompletedToday()
+      if (tasks.length === 0) {
+        logger.info('No completed tasks today; skipping AI call and flow item creation')
+        return { result: true }
+      }

38-50: Bound cost and increase determinism.

Mirror weekly: set user, low temperature, and max_tokens.

Apply:

       const response = await client.chat.completions.create({
         model: ai.modelPro,
+        user: 'ai:daily-report',
+        temperature: 0.2,
+        max_tokens: 600,
         messages: [
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4cab6c and 67ee286.

📒 Files selected for processing (2)
  • apps/web-app/server/tasks/ai/daily-report.ts (2 hunks)
  • apps/web-app/server/tasks/ai/weekly-report.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web-app/server/tasks/ai/weekly-report.ts (1)
packages/database/src/tables.ts (1)
  • tasks (326-338)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (5)
apps/web-app/server/tasks/ai/weekly-report.ts (2)

52-57: Nice: client hardened with timeout and retries.

Matches earlier guidance; reduces hang risk.


15-18: Environment guard: confirm desired behavior for staging.

If you run scheduled tasks in staging, consider a runtime flag (ai.enabled) instead of NODE_ENV check.

apps/web-app/server/tasks/ai/daily-report.ts (3)

12-12: Meta description tweak LGTM.


31-36: Good hardening: timeout and limited retries added.

Brings parity with weekly task.


46-49: Data sent to OpenAI: confirm policy.

Daily payload includes internal task text. Ensure this is approved or gate via config.

Comment on lines +67 to +69
role: 'user',
content: JSON.stringify(preparedTasks),
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

PII to third‑party AI: anonymize or gate behind config.

preparedTasks includes names/surnames/captions. Add an opt‑in (e.g., runtimeConfig.ai.allowPII) or hash/initials before sending.

Example guard:

if (!ai.allowPII) {
  preparedTasks.forEach(t => {
    if (t.performer) {
      t.performer = {
        ...t.performer,
        name: undefined,
        surname: undefined,
        caption: undefined,
      }
    }
  })
}
🤖 Prompt for AI Agents
In apps/web-app/server/tasks/ai/weekly-report.ts around lines 67-69, the code
sends preparedTasks (which may contain names, surnames, captions) to a
third-party AI; add a runtime guard or anonymization so PII is not sent by
default. Implement a check like runtimeConfig.ai.allowPII (opt-in) and if false
iterate preparedTasks to remove or replace performer.name, performer.surname and
performer.caption (or replace with initials/hash) before stringifying and
sending to the AI endpoint. Ensure the guard is applied immediately before
building the message payload so no PII is included unless explicitly allowed.

Comment on lines +85 to +89
} catch (error) {
errorResolver(error)
}

return { result: true }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Don’t mask failures; propagate to the scheduler.

Currently returns success after errors, hiding incidents.

Apply:

-    } catch (error) {
-      errorResolver(error)
-    }
-
-    return { result: true }
+    } catch (error) {
+      errorResolver(error)
+      throw error
+    }
+
+    return { result: true }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (error) {
errorResolver(error)
}
return { result: true }
} catch (error) {
errorResolver(error)
throw error
}
return { result: true }
🤖 Prompt for AI Agents
In apps/web-app/server/tasks/ai/weekly-report.ts around lines 85 to 89, the
catch block swallows errors and the function always returns success which hides
failures from the scheduler; change the catch to await/errorResolver(error) if
it returns a promise (or call it synchronously if not) and then rethrow the
caught error so the scheduler receives the failure (do not return { result: true
} after an exception).

@hmbanan666 hmbanan666 merged commit 1867eab into main Sep 22, 2025
8 checks passed
@hmbanan666 hmbanan666 deleted the weekly-report branch September 22, 2025 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant